<script>
    import E from "wangeditor";
    import addExpands from './extends/menus';
    import {jude} from '@bixiao/utils';
    import {toFirstUpperCase} from './utils';
    import {Row, Col, Button} from 'element-ui';
    import TlUploadProgress from '@components/tl-ui/upload-progress';
    import TlAliOssUpload from '@components/tl-ui/ali-oss-upload';
    import {cloneDeep, debounce} from 'lodash';
    import qs from 'qs';

    const MENUS = [
        'head',
        'bold',
        'fontSize',
        'fontName',
        'italic',
        'underline',
        'strikeThrough',
        'indent',
        'lineHeight',
        'foreColor',
        'backColor',
        'link',
        'list',
        'justify',
        'quote',
        'emoticon',
        'image',
        'video',
        'table',
        'code',
        'splitLine',
        'undo',
        'redo'
    ]

    // 默认布局（全部布局）
    const DEFAULT_LAYOUT = ["container", "toolbar"]

    // 上传枚举(自定义)
    const UPLOAD_ENUMS = ["uploadImage", "uploadLibrary", "uploadAttachment", "ossImage", "ossAttachment"]
    const Attachment_UPLOAD_ENUMS = ["uploadAttachment", "ossAttachment"];

    // 表单枚举
    const FORM_ENUMS = {
        "upload_list": [],
        "html": "",
        "html_json": {},
        "personnels": []
    }

    export default {
        name: "tl-rich-text",
        components: {
            [Row.name]: Row,
            [Col.name]: Col,
            [Button.name]: Button,
            [TlUploadProgress.name]: TlUploadProgress,
            [TlAliOssUpload.Upload.name]: TlAliOssUpload.Upload,
        },
        props: {
            value: {
                type: String,
                default: ""
            },
            isOss: {
                type: Boolean,
                default: false
            },
            menus: {
                type: Array,
                default: () => ["emoji", "ossImage", "uploadLibrary", "ossAttachment", ...MENUS]
            },
            layout: {
                type: [String, Array],
                default: () => [...DEFAULT_LAYOUT]
            },
            // 是否显示Toolbar中按钮
            showToolbarBtn: {
                type: Boolean,
                default: true
            },
            maxHeight: {
                type: [Number, String],
                default: "auto"
            },
            height: {
                type: [Number, String],
                default: "auto"
            },
            minHeight: {
                type: [Number, String],
                default: 60
            },
            placeholder: {
                type: String,
                default: '请输入正文'
            },
            /**
             * 是否图文分离
             **/
            imageAndTextSeparation: {
                type: Boolean,
                default: true
            },
            // 上传服务信息
            uploadOptions: {
                type: [Array, Object],
                default: () => {
                    return {
                        server: "/rich-text-upload",
                        maxSize: "1MB",
                        minSize: 1,
                        maxLength: 10,
                        minLength: 1,
                        params: {},
                        headers: {}
                    }
                }
            },
            zIndex: {
                type: Number,
                default: 500
            },
            gutter: {
                type: Number,
                default: 10
            },
            focus: {
                type: Boolean,
                default: true
            },
            /**
             * 插入网络图片的功能
             **/
            showLinkImg: {
                type: Boolean,
                default: true
            },
            /**
             * 默认人员列表
             **/
            default_personnels: {
                type: Array,
                default: () => []
            },
            // change
            changeDelay: {
                type: Number,
                default: 1000
            }
        },
        watch: {
            value: {
                handler(n) {
                    // this.uploadHtml(n)
                },
                immediate: true
            }
        },
        computed: {
            files_list() {
                const _upload_list = this.upload_list;
                const upload_list_map = this.upload_list_map;
                let upload_list = [];
                for (let [k, val] of [...upload_list_map]) {
                    if (Array.isArray(val)) {
                        upload_list = upload_list.concat(val)
                    }
                }
                return upload_list.map(value => {
                    const {progress} = value;
                    let percentage = 0, bytesUploaded = 0, uploadStarted, status = undefined;
                    if (progress && Object.keys(progress).length > 0 && !value.uploadURL) {
                        percentage = progress.percentage;
                        bytesUploaded = progress.bytesUploaded;
                    } else if (value.uploadURL) {
                        percentage = 100;
                    }
                    if (value.isUploadError) {
                        status = "exception"
                    } else if (value.isUploadSuccess) {
                        status = "success"
                    } else if (value.isUploadStart) {

                    }
                    return {
                        ...value,
                        url: value.uploadURL || value.url,
                        percentage,
                        bytesUploaded,
                        status
                    }
                });
            },
            upload_path() {
                return 'pm/rich-text'
            }
        },
        data() {
            return {
                editor: null,
                upload_list_map: new Map(),
                loading: false,
                isDestroy: false,
                upload_accept: '',
                // form
                upload_list: [],
                html: '',
                html_json: {},
                personnels: [],
            }
        },
        render() {
            let layout = this.layout;
            if (typeof layout === "string") layout = layout.split(",");
            if (!layout.includes('container')) layout.push('container');
            // 获取是否存在的布局 并且判断是否为字符串
            layout = layout.filter(value =>
                DEFAULT_LAYOUT.includes(value)
                && typeof value === "string"
                && value.replace(/(^\s*)|(\s*$)/g, "").length > 0
            ).map(value => value.replace(/(^\s*)|(\s*$)/g, ""));
            // 去重
            layout = [...new Set(layout)];
            const fIndex = layout.findIndex(value => value === 'container');
            if (fIndex > -1 && !layout.includes('progress')) {
                layout.splice(fIndex + 1, 0, 'progress')
            }
            const richTextStyle = {
                '--rich-text__container_min_h': typeof this.minHeight === "string" ? this.minHeight : this.minHeight + 'px',
                'flex-wrap': "wrap"
            }
            if (this.isDestroy) return null
            return (
                <el-row gutter={this.gutter} type="flex" style={richTextStyle}
                        justify="space-between" class="tl-rich-text">
                    {
                        layout.map(value => {
                            return (
                                <el-col span={24}>
                                    {
                                        value === "progress" ?
                                            this.addProgressDom(value) :
                                            this.addOtherDom(value)
                                    }
                                </el-col>
                            )
                        })
                    }
                </el-row>
            )
        },
        mounted() {
            this.personnels = this.default_personnels;
            this.createEditor();
        },
        methods: {
            createEditor() {
                const that = this
                // 创建编辑器
                if (this.editor) this.destroyEditor();
                that.$nextTick(function () {
                    const editor = new E(this.$refs.toolbar, this.$refs.container)
                    // 默认情况下，只有 IE 和 旧版 Edge 会使用兼容模式，如果需要在其它浏览器上也使用兼容模式，可以在函数内进行判定
                    editor.config.compatibleMode = function () {
                        // 返回 true 表示使用兼容模式；返回 false 使用标准模式
                        return true
                    }

                    editor.config.showLinkImg = this.showLinkImg
                    // 设置编辑区域高度为 500px
                    editor.config.height = 500

                    editor.config.zIndex = this.zIndex
                    // 是否自动 focus
                    // editor.config.focus = false
                    // 当我们使用兼容模式的时候，可以自定义记录的频率，默认 200 ms
                    editor.config.onchangeTimeout = 10 // 修改为 500 ms
                    // 还可以修改历史记录的步数。默认 30 步
                    editor.config.historyMaxSize = 50 // 修改为 50 步
                    // 配置 onchange 回调函数
                    editor.config.onchange = function (newHtml) {
                        that.html = newHtml
                        that.html_json = editor.txt.getJSON()
                        console.log('change 之后最新的 html', newHtml)
                        // console.log('change 之后最新的 text', editor.txt.text())
                        // console.log('change 之后最新的 json', that.html_json);
                        that.setJSON(that.html_json, editor);
                        that.handleChange();
                    }
                    editor.config.onblur = function (newHtml) {
                        console.log('onblur', newHtml) // 获取最新的 html 内容
                        // that.html = newHtml
                        // that.html_json = editor.txt.getJSON()
                        that.handleChange();
                    }
                    // editor.config.onfocus = function (newHtml) {
                    //     console.log('onfocus', newHtml) // 获取最新的 html 内容
                    // }
                    // 自定义检查插入的链接
                    editor.config.linkCheck = function (text, link) {
                        // 以下情况，请三选一

                        // 1. 返回 true ，说明检查通过
                        return true

                        // // 2. 返回一个字符串，说明检查未通过，编辑器会阻止链接插入。会 alert 出错误信息（即返回的字符串）
                        // return '链接有 xxx 错误'

                        // 3. 返回 undefined（即没有任何返回），说明检查未通过，编辑器会阻止链接插入。
                        // 此处，你可以自定义提示错误信息，自由发挥
                    }
                    // 自定义检查插入图片的链接
                    editor.config.linkImgCheck = function (imgSrc) {
                        // 以下情况，请三选一

                        // 1. 返回 true ，说明检查通过
                        return true

                        // // 2. 返回一个字符串，说明检查未通过，编辑器会阻止图片插入。会 alert 出错误信息（即返回的字符串）
                        // return '图片 src 有 xxx 错误'

                        // 3. 返回 undefined（即没有任何返回），说明检查未通过，编辑器会阻止图片插入。
                        // 此处，你可以自定义提示错误信息，自由发挥
                    }
                    editor.txt.eventHooks.dropEvents.push(this.dropEvents)
                    editor.txt.eventHooks.deleteUpEvents.push(this.deleteUpEvents)

                    if (that.isOss) {
                        // oss 上传
                        editor.config.ossUploadClick = that.handleOssUploadClick
                    }


                    this.updateMenus(editor)
                    this.setPlaceholder(editor)
                    this.setUploadOptions(editor)
                    editor.config.showLinkImg = false;
                    editor.create();
                    this.uploadHtml(that.value, editor);
                    editor.initSelection(true);
                    this.editor = editor;
                    // const toolbar = $(this.$el).find('.tl-rich-text__toolbar .w-e-menu')
                    // const container = $(this.$el).find('.tl-rich-text__container')
                    // toolbar && toolbar.one('click', () => {
                    //     const dom = container.find(".w-e-text-container > div")
                    //     dom.focus()
                    //     dom.blur()
                    // })
                    this.$emit('load', editor, this)
                })
            },
            isViewerPdf(txt = '') {
                return txt.indexOf('/bixiao-web-static/web/pdf/generic/web/viewer.html') > -1
            },
            isHttp(href = '') {
                return /^https?:\/\/(([a-zA-Z0-9_-])+(\.)?)*(:\d+)?(\/((\.)?(\?)?=?&?[a-zA-Z0-9_-](\?)?)*)*$/i.test(href)
            },
            getJSON(json) {
                let flag = false;
                const list = json.filter(value => typeof value === "string" || (value && value.tag));
                for (let value of list) {
                    if (Array.isArray(value.children)) {
                        const children = this.getJSON(value.children, true);
                        if (children) {
                            flag = true
                            value.children = children;
                        }
                    }
                    if (value.tag === 'a') {
                        const attrs = cloneDeep(value.attrs);
                        for (let val of attrs) {
                            if (val.name === 'href' && this.isHttp(val.value)) {
                                if (/\.pdf$/i.test(val.value) && !this.isViewerPdf(val.value)) {
                                    flag = true;
                                    const query = qs.stringify({file: val.value})
                                    val.value = `https://res.tuoluocard.com/bixiao-web-static/web/pdf/generic/web/viewer.html?${query}`
                                }
                                break;
                            } else if (val.name === 'data-user_id' && val.value > -1) {
                                this.personnels.push(this.$util.getNumber(val.value, 0))
                                // const names = value.attrs.map(value => value.name);
                                break;
                            }
                        }
                    }
                }
                console.log(flag === true && Array.isArray(list) && list.length > 0, list);
                return flag === true && Array.isArray(list) && list.length > 0 ? list : null;
            },
            setJSON(json, editor = this.editor) {
                if (Array.isArray(json) && json.length && editor) {
                    this.personnels = [];
                    const data = this.getJSON(json);
                    if (data) editor.txt.setJSON(data);
                }
            },
            uploadHtml(html, editor = this.editor) {
                if (typeof html === "string" && editor) {
                    const txt = editor.txt.html();
                    if (txt !== html) {
                        editor.txt.html(html);
                        editor.initSelection(true);
                    }
                }
            },
            addOtherDom(value) {
                const maxHeight = this.maxHeight;
                const height = this.height;
                let containerStyle = {
                    maxHeight: maxHeight || '120px',
                    height: height
                };
                if (typeof maxHeight === "number") {
                    containerStyle.maxHeight = `${maxHeight}px`
                }
                if (typeof height === "number") {
                    containerStyle.height = `${height}px`
                }
                const showToolbarBtn = this.showToolbarBtn;
                if (showToolbarBtn === true && value === 'toolbar') {
                    return <div class="tl-rich-text__toolbar_btn">
                        <div class={`tl-rich-text__${value}`}>
                            <div ref={value}/>
                        </div>
                        <div class="tl-rich-text__tbtns">
                            <el-button loading={this.loading} type="primary" size="mini" plain
                                       onClick={this.handleSubmit}>发布
                            </el-button>
                        </div>
                    </div>
                } else {
                    return <div class={`tl-rich-text__${value}`}>
                        <div ref={value} style={value === 'container' ? containerStyle : {}}/>
                    </div>
                }
            },
            handleOssUploadClick({accept} = {}) {
                this.upload_accept = accept;
                this.$nextTick(function () {
                    this.$refs.aliUpload && this.$refs.aliUpload.btnTrigger()
                })
            },
            handleFilesComplete(list, isLibrary, isChange = true) {
                const files = list.map(value => {
                    if (value.raw) {
                        return {
                            type: value.raw.type,
                            name: value.raw.filename || value.raw.name,
                            size: value.raw.size,
                            id: value.raw.uid,
                            url: value.url,
                            isOuterFile: true,
                            is_oss: true
                        }
                    } else {
                        return {
                            name: value.file_name || value.original_name,
                            size: value.size,
                            type: value.content_type,
                            id: value.id || value.attachment_id,
                            url: value.url,
                            isOuterFile: true,
                            isLibrary: isLibrary,
                            library_id: value.id
                        }
                    }
                });
                this.addFiles(files);
            },
            addProgressDom(value) {
                const menus = this.menus;
                // 是否需要添加上传文件进度
                const isAddUpload = UPLOAD_ENUMS.some(value => menus.includes(value));
                const files_list = this.files_list;
                const task_id = this.task_id;
                if (isAddUpload) {
                    if (this.isOss) {
                        return <tl-ali-oss-upload
                            accept={this.upload_accept}
                            ref="aliUpload"
                            drag={false}
                            hide-success-file
                            upload-path={this.upload_path}
                            default-file-list={files_list}
                            list-action-icon={task_id > 0 ? 'tl-icon-download' : 'tl-icon-cancel'}
                            note=""
                            class="tl-rich-text__upload_progress"
                            list={files_list}
                            maxHeight={138}
                            maxFileSize="100MB"
                            limit={30}
                            onchange-all={this.handleFilesComplete}
                            onclick-action={this.handleRemoveFile}
                            action-icon="tl-rich-text__cancel tl-icon-cancel"/>
                    } else if (files_list.length > 0) {
                        return <tl-upload-progress
                            class="tl-rich-text__upload_progress"
                            list={files_list}
                            maxHeight={138}
                            onretry-upload={this.handleRetryUpload}
                            onclick-action={this.handleRemoveFile}
                            action-icon="tl-rich-text__cancel tl-icon-cancel"/>
                    }
                }
                return null;
            },
            updateMenus(editor = this.editor) {
                if (!editor) return;
                addExpands(editor);// 添加扩展菜单
                editor.config.menus = [...new Set(this.menus)];
            },
            setPlaceholder(editor = this.editor) {
                if (!editor) return;
                editor.config.placeholder = this.placeholder;
            },
            setUploadOptions(editor) {
                if (!editor) return;
                let uploadOptions = this.uploadOptions;
                for (let val of UPLOAD_ENUMS) {
                    if (Array.isArray(uploadOptions)) {
                        let find = this.uploadOptions.find(value => `upload${toFirstUpperCase(value.key)}` === val)
                        if (find && find.data) uploadOptions = find.data;
                        else continue;
                    }
                    if (Object.prototype.toString.call(uploadOptions) === '[object Object]' && Object.keys(uploadOptions).length > 0) {
                        for (let k of Object.keys(uploadOptions)) {
                            const v = uploadOptions[k]
                            if (!jude.isEmpty(v)) editor.config[`${val}${toFirstUpperCase(k)}`] = uploadOptions[k]
                        }
                    }
                }
                // 是否图文分离
                editor.config.customUploadImgSeparate = this.imageAndTextSeparation;
                // 设置上传监听
                editor.config.customUploadMonitor = {
                    upload: ({id, fileIDs}) => {

                    },
                    'upload-progress': (file, progress) => {

                    },
                    complete: (result, uppy) => {

                    },
                    "file-change": this.addFiles
                }
            },
            /**
             * 重新上传
             * @param fileID
             * @param file
             */
            handleRetryUpload(fileID, file) {
                const find = this.files_list.find(value => value.id === fileID)
                find && find.instance && find.instance.retryUpload(fileID);
            },
            /**
             * 删除文件
             * @param fileID
             * @param file
             */
            handleRemoveFile(fileID, file) {
                if (file) {
                    switch (true) {
                        case file.instance && Object.keys(file.instance).length > 0:
                            file.instance.removeFile(fileID);
                            break;
                        default:
                            const otherFiles = this.upload_list_map.get('other');
                            if (Array.isArray(otherFiles)) {
                                const fIndex = otherFiles.findIndex(file => file.id === fileID);
                                if (fIndex > -1) {
                                    otherFiles.splice(fIndex, 1)
                                    this.$refs.aliUpload.removeFile(fileID);
                                    // this.upload_list_map.set('other', otherFiles);
                                    this.upload_list.push(fileID);
                                }
                            }
                    }
                }
            },
            // 添加文件
            addFiles(files, instance) {
                if (!Array.isArray(files)) return;
                files = files.filter(value => value.id)
                if (instance) files.forEach(value => value.instance = instance)
                let id = "other";
                if (typeof instance === "string") {
                    id = instance;
                } else if (instance && instance.getID) {
                    id = instance.getID();
                }
                if (id === "other") {
                    const otherFiles = this.upload_list_map.get(id);
                    if (Array.isArray(otherFiles)) {
                        const filesIDs = files.map(value => value.id)
                        const fileList = otherFiles.filter(value => !filesIDs.includes(value.id))
                        files = files.concat(fileList);
                    }
                }
                this.upload_list_map.set(id, files);
                this.upload_list.push(id);
                this.handleChange();
            },
            destroyEditor(next) {
                // 销毁编辑器
                if (this.editor) {
                    this.editor.destroy()
                    this.editor = null
                }
            },
            clear() {
                const editor = this.editor
                this.upload_list_map = new Map();
                for (let key of Object.keys(FORM_ENUMS)) {
                    this[key] = FORM_ENUMS[key]
                }
                if (editor) {
                    editor.txt.clear();
                    if (Array.isArray(editor.txt.eventHooks.clearContentEvents)) {
                        editor.txt.eventHooks.clearContentEvents.forEach(func => {
                            typeof func === "function" && func();
                        })
                    }
                }
                this.$refs.aliUpload.clear(null, true)
            },
            filesChange(uppy) {

            },
            deleteUpEvents(e) {
                // 键盘事件
                console.log('键盘事件', e)
            },
            dropEvents(e) {
                // 拖拽事件
                e.preventDefault(); //阻止默认
                let fileList = [...e.dataTransfer.files]; //获取拖拽的文件列表
                const isAddUpload = UPLOAD_ENUMS.some(value => this.menus.includes(value));
                if (isAddUpload) {
                    console.log('拖拽事件', e, fileList);
                    const isAttachmentUpload = Attachment_UPLOAD_ENUMS.some(value => this.menus.includes(value));
                    if (this.isOss) {
                        const files = this.getImageList(fileList, isAttachmentUpload);
                        this.$refs.aliUpload.addFiles(files);
                    } else {
                        this.customUploadImg(fileList)
                    }
                }
            },
            getImageList(fileList, isAttachmentUpload = false) {
                if (!isAttachmentUpload) {
                    const files = fileList.filter(value => /^image\/(\s|\S){1,}$/.test(value.type));
                    if (files.length > 0) {
                        if (files.length !== fileList.length) {
                            const list = fileList.filter(value => !/^image\/(\s|\S){1,}$/.test(value.type));
                            list.forEach((file) => {
                                setTimeout(() => {
                                    this.$ele.$message.warning(`【${file.name}】不是图片`)
                                }, 300)
                            });
                        }
                        return files;
                    }
                    return [];
                }
                return fileList
            },
            customUploadImg(resultFiles, insertImgFn) {
                const imageList = this.getImageList(resultFiles);
                const customUploadImg = this.editor && this.editor.config.customUploadImg;
                if (imageList.length > 0 && typeof customUploadImg === "function") {
                    customUploadImg(imageList, insertImgFn)
                }
            },
            getEditorData() {
                const {html, html_json, personnels} = this
                const files_list = this.files_list.filter(value => value.url).map(value => {
                    const val = {...value}
                    delete val.instance;
                    return val
                });
                return {
                    html,
                    html_json,
                    personnels: cloneDeep(personnels),
                    files_list: cloneDeep(files_list)
                };
            },
            /**
             * 实时更新数据
             */
            handleChange() {
                this.$nextTick(debounce(function () {
                    const data = this.getEditorData()
                    // 更新v-modal 数据
                    this.$emit("change", data);
                    this.$emit("input", data.html);
                }, this.changeDelay))
            },
            // 富文本提交事件
            handleSubmit(bol) {
                const that = this
                const data = this.getEditorData()
                this.$emit('submit', data, that)
                // 更新v-modal 数据
                this.$emit('input', data.html)
                bol !== false && this.clear()
            }
        },
        beforeDestroy() {
            if (this.editor) this.destroyEditor();
        }
    }
</script>

<style scoped lang="scss">
    .tl-rich-text {
        position: relative;
        border-radius: 4px;
        border: 1px solid #F1F1F1;
        background: #fff;

        ::v-deep {
            .w-e-toolbar, .w-e-text-container, .w-e-menu-panel {
                background: transparent;
            }
        }

        .tl-rich-text__container {
            padding: 0;
            min-height: var(--rich-text__container_min_h);

            ::v-deep {
                .w-e-text-container {
                    overflow: unset;
                    overflow-y: auto;
                    min-height: var(--rich-text__container_min_h);
                }

                .w-e-text {
                    padding: 10px 14px;

                    p,
                    h1, h2, h3, h4, h5,
                    table,
                    pre {
                        margin: 0;
                        letter-spacing: 0.56px;
                        line-height: inherit;
                        font-size: inherit;
                    }

                    /*修改滚动条样式*/
                    &::-webkit-scrollbar {
                        width: 3px;
                        height: 3px;
                    }
                }

                .placeholder {
                    left: 14px;
                    top: 10px;
                    color: #BFBFBF;
                    line-height: inherit;
                    font-size: inherit;
                    letter-spacing: 0.56px;
                }
            }
        }

        .tl-rich-text__toolbar_btn {
            display: flex;
            align-items: center;

            .tl-rich-text__toolbar {
                flex: 1
            }

            .tl-rich-text__tbtns {
                padding: 0 14px;

                ::v-deep {
                    .el-button--primary {
                        border-color: #0075ff;

                        &.is-plain {
                            background: transparent;

                            &:hover {
                                background: #0075ff;
                            }
                        }

                        &.el-button--mini {
                            padding: 5px 11px;
                            border-radius: 4px;
                        }
                    }
                }
            }
        }

        .tl-rich-text__toolbar {
            padding: 0 14px;

            ::v-deep {
                .w-e-toolbar {
                    margin-left: -6px;
                    margin-right: -6px;
                    min-height: 40px;
                    padding: 0;
                    align-items: center;

                    .w-e-menu {
                        width: unset;
                        height: unset;
                        padding: 0;

                        > i, > span {
                            padding: 6px;
                            font-size: 15px;
                            color: #AAB3BD;
                        }

                        .w-e-icon-image {
                            display: none;
                        }
                    }
                }
            }
        }

        ::v-deep {
            .w-e-menu.tl-rich-text-menu {
                .w-e-panel-container {
                    margin-left: 0 !important;
                    border-color: #F1F1F1;
                    top: 8px;
                }
            }
        }
    }

    .tl-rich-text__upload_progress {
        ::v-deep {
            .tl-upload-progress__item {
                border: none;
                padding: 6px 0;

                + .tl-upload-progress__item {
                    margin-top: 0;
                }

                .action {
                    .el-button i.tl-rich-text__cancel {
                        width: 20px;
                        height: 20px;
                        line-height: 20px;
                        text-align: center;
                        display: inline-block;

                        &:before {
                            font-size: 12px;
                        }
                    }
                }
            }
        }
    }


</style>
